//! Protocol Buffers Remote Actor Demo
//!
//! This example demonstrates how to use Protocol Buffers for serialization in actor communication.
//! It shows how to:
//! 1. Define Protocol Buffer message types
//! 2. Convert between Protocol Buffer messages and actor messages
//! 3. Implement Protocol Buffers for actor message serialization and simulated remote communication
//!
//! Run with:
//! ```bash
//! cargo run --example protobuf_remote_demo --features "remote protobuf"
//! ```

#[cfg(all(feature = "remote", feature = "protobuf"))]
mod example {
    use kameo::serialization::{self, SerializationConfig, SerializationFormat};
    use prost::Message as ProstMessage;
    use serde::{Deserialize, Serialize};
    
    // Define our counter actor (simplified without actual remote functionality)
    #[derive(Debug)]
    pub struct CounterActor {
        count: i64,
        name: String,
    }
    
    impl CounterActor {
        pub fn new(name: &str) -> Self {
            Self {
                count: 0,
                name: name.to_string(),
            }
        }
    }
    
    // Define Protocol Buffer message for increment
    #[derive(Clone, ProstMessage)]
    pub struct IncrementProto {
        #[prost(uint32, tag = "1")]
        pub amount: u32,
        
        #[prost(string, tag = "2")]
        pub description: String,
    }
    
    // Add Serialize/Deserialize traits to make it compatible with actor system
    #[derive(Clone, Debug, Serialize, Deserialize)]
    pub struct Increment {
        amount: u32,
        description: String,
    }
    
    // Implement conversion between Protobuf and Serde formats
    impl From<IncrementProto> for Increment {
        fn from(proto: IncrementProto) -> Self {
            Self {
                amount: proto.amount,
                description: proto.description,
            }
        }
    }
    
    impl From<Increment> for IncrementProto {
        fn from(inc: Increment) -> Self {
            Self {
                amount: inc.amount,
                description: inc.description,
            }
        }
    }
    
    // Define Protocol Buffer message for decrement
    #[derive(Clone, ProstMessage)]
    pub struct DecrementProto {
        #[prost(uint32, tag = "1")]
        pub amount: u32,
        
        #[prost(string, tag = "2")]
        pub reason: String,
    }
    
    // Add Serialize/Deserialize traits to make it compatible with actor system
    #[derive(Clone, Debug, Serialize, Deserialize)]
    pub struct Decrement {
        amount: u32,
        reason: String,
    }
    
    // Implement conversion between Protobuf and Serde formats
    impl From<DecrementProto> for Decrement {
        fn from(proto: DecrementProto) -> Self {
            Self {
                amount: proto.amount,
                reason: proto.reason,
            }
        }
    }
    
    impl From<Decrement> for DecrementProto {
        fn from(dec: Decrement) -> Self {
            Self {
                amount: dec.amount,
                reason: dec.reason,
            }
        }
    }
    
    // Define Protocol Buffer for the response
    #[derive(Clone, ProstMessage)]
    pub struct CounterResponseProto {
        #[prost(int64, tag = "1")]
        pub new_value: i64,
        
        #[prost(string, tag = "2")]
        pub actor_name: String,
        
        #[prost(bool, tag = "3")]
        pub success: bool,
    }
    
    // The response type that will be used in the actor system
    #[derive(Clone, Debug, Serialize, Deserialize)]
    pub struct CounterResponse {
        new_value: i64,
        actor_name: String,
        success: bool,
    }
    
    // Implement conversion between Protobuf and Serde formats
    impl From<CounterResponseProto> for CounterResponse {
        fn from(proto: CounterResponseProto) -> Self {
            Self {
                new_value: proto.new_value,
                actor_name: proto.actor_name,
                success: proto.success,
            }
        }
    }
    
    impl From<CounterResponse> for CounterResponseProto {
        fn from(resp: CounterResponse) -> Self {
            Self {
                new_value: resp.new_value,
                actor_name: resp.actor_name,
                success: resp.success,
            }
        }
    }
    
    // Simulate actor message handling
    impl CounterActor {
        pub fn handle_increment(&mut self, msg: Increment) -> CounterResponse {
            println!("Incrementing counter by {} - '{}'", msg.amount, msg.description);
            self.count += msg.amount as i64;
            
            CounterResponse {
                new_value: self.count,
                actor_name: self.name.clone(),
                success: true,
            }
        }
        
        pub fn handle_decrement(&mut self, msg: Decrement) -> CounterResponse {
            println!("Decrementing counter by {} - reason: '{}'", msg.amount, msg.reason);
            self.count -= msg.amount as i64;
            
            CounterResponse {
                new_value: self.count,
                actor_name: self.name.clone(),
                success: true,
            }
        }
    }
    
    // Simulate remote communication channel
    pub struct RemoteChannel;
    
    impl RemoteChannel {
        // Simulate sending a message over the network
        pub fn send<T: ProstMessage + Clone>(msg: &T) -> Vec<u8> {
            // In a real system, this would send the bytes over the network
            msg.encode_to_vec()
        }
        
        // Simulate receiving a message from the network
        pub fn receive<T: ProstMessage + Default>(bytes: &[u8]) -> Result<T, prost::DecodeError> {
            // In a real system, this would receive bytes from the network
            T::decode(bytes)
        }
    }
    
    // Helper function to display binary data as hex
    fn hex_string(bytes: &[u8]) -> String {
        bytes.iter()
            .map(|b| format!("{:02x}", b))
            .collect::<Vec<_>>()
            .join("")
    }
    
    // Simulate a complete remote actor communication
    pub fn simulate_remote_communication() {
        println!("\n--- Simulating Remote Communication ---");
        
        // Create actors (simulating different processes or machines)
        let mut server_actor = CounterActor::new("ServerCounter");
        let mut client_actor = CounterActor::new("ClientCounter");
        
        // Client creates a message to send
        let increment = Increment {
            amount: 25,
            description: "Remote increment".to_string(),
        };
        
        println!("Client preparing to send: amount={}, description='{}'", 
                 increment.amount, increment.description);
        
        // Convert to Protocol Buffer format (required for network transmission)
        let increment_proto: IncrementProto = increment.into();
        
        // Simulate network transmission
        let network_bytes = RemoteChannel::send(&increment_proto);
        println!("Message serialized to {} bytes and sent over network", network_bytes.len());
        
        // Server receives the bytes and decodes
        let received_proto = RemoteChannel::receive::<IncrementProto>(&network_bytes).unwrap();
        let received_increment: Increment = received_proto.into();
        
        println!("Server received: amount={}, description='{}'", 
                 received_increment.amount, received_increment.description);
        
        // Server processes the message
        let response = server_actor.handle_increment(received_increment);
        println!("Server processed message, new count: {}", response.new_value);
        
        // Server sends response back
        let response_proto: CounterResponseProto = response.into();
        let response_bytes = RemoteChannel::send(&response_proto);
        println!("Response serialized to {} bytes and sent over network", response_bytes.len());
        
        // Client receives the response
        let received_response_proto = RemoteChannel::receive::<CounterResponseProto>(&response_bytes).unwrap();
        let received_response: CounterResponse = received_response_proto.into();
        
        println!("Client received response: new_value={}, actor_name='{}', success={}", 
                 received_response.new_value, received_response.actor_name, received_response.success);
                 
        // Update client's local state
        client_actor.count = received_response.new_value;
        println!("Client updated local state, count: {}", client_actor.count);
    }
    
    // Example that demonstrates Protocol Buffers for remote actor messages
    pub fn run() {
        println!("Protocol Buffers Remote Actor Demo\n");
        
        // Configure to use MessagePack as serialization format for comparison
        let config = SerializationConfig::new()
            .with_format(SerializationFormat::MessagePack);
        serialization::init_serialization(config);
        
        // Create our actor
        let mut counter = CounterActor::new("MyCounter");
        
        // Create messages
        let increment = Increment {
            amount: 10,
            description: "Initial increment".to_string(),
        };
        
        let decrement = Decrement {
            amount: 3,
            reason: "Testing decrement".to_string(),
        };
        
        // 1. Protocol Buffers Serialization
        println!("--- Protocol Buffers Serialization ---");
        
        // Convert to Protocol Buffer format first
        let increment_proto: IncrementProto = increment.clone().into();
        
        // Serialize to binary
        let proto_bytes = increment_proto.encode_to_vec();
        println!("Protocol Buffer serialized size: {} bytes", proto_bytes.len());
        println!("Hex: {}", hex_string(&proto_bytes));
        
        // Deserialize back from binary
        let decoded_proto = IncrementProto::decode(proto_bytes.as_slice()).unwrap();
        
        // Convert back to actor message
        let decoded_increment: Increment = decoded_proto.into();
        println!("Decoded increment: amount={}, description='{}'", 
                 decoded_increment.amount, decoded_increment.description);
        
        // 2. MessagePack Serialization (for comparison)
        println!("\n--- MessagePack Serialization ---");
        
        // Serialize using MessagePack
        let messagepack_bytes = serialization::serialize(&increment).unwrap();
        println!("MessagePack serialized size: {} bytes", messagepack_bytes.len());
        println!("Hex: {}", hex_string(&messagepack_bytes));
        
        // Deserialize using MessagePack
        let messagepack_decoded: Increment = serialization::deserialize(&messagepack_bytes).unwrap();
        println!("Decoded increment: amount={}, description='{}'", 
                 messagepack_decoded.amount, messagepack_decoded.description);
        
        // 3. Simulate actor message handling
        println!("\n--- Simulated Actor Message Handling ---");
        
        // Process increment message
        let increment_response = counter.handle_increment(increment);
        println!("After increment: count={}, actor={}", 
                 increment_response.new_value, increment_response.actor_name);
        
        // Process decrement message
        let decrement_response = counter.handle_decrement(decrement);
        println!("After decrement: count={}, actor={}", 
                 decrement_response.new_value, decrement_response.actor_name);
        
        // Serialize responses with Protocol Buffers
        let increment_response_proto: CounterResponseProto = increment_response.clone().into();
        let increment_proto_bytes = increment_response_proto.encode_to_vec();
        
        let decrement_response_proto: CounterResponseProto = decrement_response.clone().into();
        let decrement_proto_bytes = decrement_response_proto.encode_to_vec();
        
        // Serialize with MessagePack for comparison
        let increment_mp_bytes = serialization::serialize(&increment_response).unwrap();
        let decrement_mp_bytes = serialization::serialize(&decrement_response).unwrap();
        
        // Size comparison
        println!("\n--- Size Comparison ---");
        println!("Increment - Protocol Buffers size: {} bytes", proto_bytes.len());
        println!("Increment - MessagePack size: {} bytes", messagepack_bytes.len());
        println!("Increment Response - Protocol Buffers size: {} bytes", increment_proto_bytes.len());
        println!("Increment Response - MessagePack size: {} bytes", increment_mp_bytes.len());
        println!("Decrement Response - Protocol Buffers size: {} bytes", decrement_proto_bytes.len());
        println!("Decrement Response - MessagePack size: {} bytes", decrement_mp_bytes.len());
        
        let avg_ratio = (
            proto_bytes.len() as f64 / messagepack_bytes.len() as f64 +
            increment_proto_bytes.len() as f64 / increment_mp_bytes.len() as f64 +
            decrement_proto_bytes.len() as f64 / decrement_mp_bytes.len() as f64
        ) / 3.0;
        
        println!("Average size ratio (ProtoBuf:MessagePack): {:.2}x", avg_ratio);
        
        // Simulate remote communication
        simulate_remote_communication();
    }
}

fn main() {
    #[cfg(all(feature = "remote", feature = "protobuf"))]
    example::run();
    
    #[cfg(not(all(feature = "remote", feature = "protobuf")))]
    println!("This example requires both 'remote' and 'protobuf' features. Run with:\ncargo run --example protobuf_remote_demo --features \"remote protobuf\"");
} 